﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AddIn.EA.Core.Common;
using System.Text.RegularExpressions;
using AddIn.EA.Framework;

namespace AddIn.EA.Architecture.Presentation
{
    [AddInMenu("Create Standard Project - Entity")]
    public partial class CreateStandardProjectBusinessEntity : BaseAddInArchitecture
    {
        #region Local Objects
        private ComponentLogic.StandardProject.BusinessEntityStandardProject EntityEdit;
        private ComponentLogic.StandardProject.PropertyStandardProject AttributeEdit;
        private List<ComponentLogic.StandardProject.BusinessEntityStandardProject> Entities;
        #endregion

        #region Starting Form
        public CreateStandardProjectBusinessEntity(ComponentLogic.StandardProject.BusinessEntityStandardProject businessEntity, RepositoryManager repository, List<ComponentLogic.StandardProject.BusinessEntityStandardProject> entities)
        {
            InitializeComponent();
            base.LoadAddInControls += new EventHandler(CreateStandardProjectBusinessEntity_LoadAddInControls);
            this.Repository = repository;
            this.EntityEdit = businessEntity;
            this.Entities = entities;
        }
        private void CreateStandardProjectBusinessEntity_Load(object sender, EventArgs e)
        {
            StartForm();
        }
        void CreateStandardProjectBusinessEntity_LoadAddInControls(object sender, EventArgs e)
        {
            StartForm();
        }
        private void StartForm()
        {
            try
            {
                ucLayers.LayerCheckedChange += new Action(ucLayers_LayerCheckedChange);

                LoadScope();
                LoadRuleSets();
                if (EntityEdit != null)
                    LoadEntitEdition();
                else
                    EntityEdit = new AddIn.EA.Architecture.ComponentLogic.StandardProject.BusinessEntityStandardProject();
                SetMethodsButtonEnable();
            }
            catch (Exception ex)
            {
                HandleException("Inicializar a edição da Entidade", ex);
            }
        }
        void ucLayers_LayerCheckedChange()
        {
            SetMethodsButtonEnable();
        }
        #endregion

        #region Events
        private void cmdEntityMethods_Click(object sender, EventArgs e)
        {
            OpenEntityMethods();
        }
        private void cmdImportAttributes_Click(object sender, EventArgs e)
        {
            ImportEntityAttributes();
        }
        private void cmdInsertAttribute_Click(object sender, EventArgs e)
        {
            SaveEntityAttribute();
        }
        private void cmdCleanAttributeForm_Click(object sender, EventArgs e)
        {
            CleanEntityAttributesForm();
        }
        private void cmdEditAttribute_Click(object sender, EventArgs e)
        {
            EditSelectedAttribute();
        }
        private void cmdRemoveAttribute_Click(object sender, EventArgs e)
        {
            RemoveSelectedAttribute();
        }
        private void cmdSaveEntity_Click(object sender, EventArgs e)
        {
            SaveEntity();
        }
        private void lstAttributes_DoubleClick(object sender, EventArgs e)
        {
            EditSelectedAttribute();
        }
        private void ctxAttributeEdit_Click(object sender, EventArgs e)
        {
            EditSelectedAttribute();
        }
        private void ctxAttributeRemove_Click(object sender, EventArgs e)
        {
            RemoveSelectedAttribute();
        }
        private void lstAttributes_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
                RemoveSelectedAttribute();
        }
        private void cmdCleanAllLayers_Click(object sender, EventArgs e)
        {
            ucLayers.SetAllChecks(false);
        }
        private void ckAttributeEncapsulate_CheckedChanged(object sender, EventArgs e)
        {
            SetAttributeScopeState();
        }
        private void txtName_Leave(object sender, EventArgs e)
        {
            SetMethodsButtonEnable();
            ValidateExistingEntity();
        }
        private void txtNamePlural_Leave(object sender, EventArgs e)
        {
            SetMethodsButtonEnable();
        }
        private void txtName_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void txtNamePlural_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void txtAttributeName_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void txtAttributeType_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void txtAttributeStereotype_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void cmdAddRuleSet_Click(object sender, EventArgs e)
        {
            AddSelectedRuleSet();
        }
        #endregion

        #region Methods
        private void SetMethodsButtonEnable()
        {
            cmdEntityMethods.Enabled = !String.IsNullOrEmpty(txtName.Text.Trim()) &&
                                       !String.IsNullOrEmpty(txtNamePlural.Text.Trim()) &&
                                       ucLayers.IsAnyLayerChecked;
            cmdSaveEntity.Enabled = !String.IsNullOrEmpty(txtName.Text.Trim()) &&
                                       !String.IsNullOrEmpty(txtNamePlural.Text.Trim()) &&
                                       ucLayers.IsAnyLayerChecked;
        }
        private void OpenEntityMethods()
        {
            try
            {
                GetEntityData();
                CreateStandardProjectMethods wd = new CreateStandardProjectMethods(EntityEdit.Methods, EntityEdit, Repository, Entities);
                wd.ShowDialog(this);
                if (wd.Tag != null)
                    EntityEdit.Methods = wd.Tag as List<ComponentLogic.StandardProject.MethodStandardProject>;
            }
            catch (Exception ex)
            {
                HandleException("Abrir a tela de métodos da entidade.", ex);
            }
        }
        private void LoadScope()
        {
            try
            {
                String[] list = Enum.GetNames(typeof(ElementScope));
                foreach (String item in list)
                {
                    cmbScope.Items.Add(item);
                    cmbAttributeScope.Items.Add(item);
                }
                cmbScope.SelectedIndex = cmbScope.Items.Count > 0 ? 0 : -1;
                SetAttributeScopeState();
            }
            catch (Exception ex)
            {
                HandleException("Carregar as listas de escopos.", ex);
            }
        }
        private void LoadRuleSets()
        {
            try
            {
                ComponentLogic.RuleSetList rulesets = ComponentLogic.RuleSetList.LoadRuleSets();
                cmbRuleSets.DisplayMember = "Name";
                cmbRuleSets.ValueMember = "Name";
                cmbRuleSets.DataSource = rulesets.ToList();
            }
            catch (Exception ex)
            {
                HandleException("Carregar a lista de RuleSets", ex);
            }
        }
        private void SetAttributeScopeState()
        {
            if (ckAttributeEncapsulate.Checked)
            {
                cmbAttributeScope.SelectedItem = ElementScope.Private.ToString();
                cmbAttributeScope.Enabled = false;
            }
            else
                cmbAttributeScope.Enabled = true;
        }
        private void LoadEntitEdition()
        {
            try
            {
                txtName.Text = EntityEdit.Name;
                txtNamePlural.Text = EntityEdit.NamePlural;
                cmbScope.SelectedItem = EntityEdit.Scope.ToString();
                txtNotes.Text = EntityEdit.Notes;
                LoadEntityProperties();
                LoadEntityLayers();
                txtName.Enabled = false;
            }
            catch (Exception ex)
            {
                HandleException("Carregar os dados da entidade para edição.", ex);
            }
        }
        private void LoadEntityLayers()
        {
            ucLayers.SetLayersBuilders(EntityEdit.Builders);
        }
        private void SaveEntityAttribute()
        {
            try
            {
                if (ValidateAttributeForm())
                {
                    ComponentLogic.StandardProject.PropertyStandardProject attrb = GetAttributeData();
                    Int32 idx = EntityEdit.Properties.FindIndex(it => ((ComponentLogic.StandardProject.PropertyStandardProject)it).Sequence.CompareTo(attrb.Sequence) == 0);
                    if (idx != -1)
                        EntityEdit.Properties[idx] = attrb;
                    else
                        EntityEdit.Properties.Add(attrb);

                    CleanEntityAttributesForm();
                    LoadEntityProperties();
                }
            }
            catch (Exception ex)
            {
                HandleException("Inserir novo atributo.", ex);
            }
        }
        private void LoadEntityProperties()
        {
            try
            {
                lstAttributes.Items.Clear();
                if (EntityEdit.Properties != null && EntityEdit.Properties.Count > 0)
                {
                    foreach (ComponentLogic.StandardProject.PropertyStandardProject item in EntityEdit.Properties)
                    {
                        ListViewItem listItem = new ListViewItem(item.Sequence.ToString());
                        listItem.SubItems.Add(item.Name);
                        listItem.SubItems.Add(item.Type);
                        listItem.SubItems.Add(item.Scope.ToString());
                        listItem.SubItems.Add(item.Stereotype);
                        listItem.SubItems.Add(GetBooleanYesNoDescription(item.ComposeFilter));
                        listItem.SubItems.Add(GetBooleanYesNoDescription(item.ComposeKey));
                        listItem.SubItems.Add(GetBooleanYesNoDescription(item.Encapsulate));
                        listItem.SubItems.Add(GetBooleanYesNoDescription(item.IsStatic));
                        listItem.SubItems.Add(GetBooleanYesNoDescription(item.IsConstant));
                        lstAttributes.Items.Add(listItem);
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException("Carregar a lista de atributos/propriedades da classe", ex);
            }
        }
        private ComponentLogic.StandardProject.PropertyStandardProject GetAttributeData()
        {
            ComponentLogic.StandardProject.PropertyStandardProject property = new ComponentLogic.StandardProject.PropertyStandardProject()
            {
                ComposeFilter = ckAttributeUseAsFilter.Checked,
                ComposeKey = ckAttributeUseAsKey.Checked,
                Encapsulate = ckAttributeEncapsulate.Checked,
                InitialValue = txtAttributeInitialValue.Text,
                IsConstant = ckAttributeIsConstant.Checked,
                IsStatic = ckAttributeIsStatic.Checked,
                Notes = txtAttributeNotes.Text,
                Scope = (ElementScope)Enum.Parse(typeof(ElementScope), cmbAttributeScope.SelectedItem.ToString()),
                Stereotype = txtAttributeStereotype.Text,
                Type = cmbAttributeTypes.SelectedItem,
                RuleSets = GetAttributeRuleSetsList(txtAttributeRuleSets.Text)
            };

            if (AttributeEdit != null)
                property.Sequence = AttributeEdit.Sequence;
            else
                property.Sequence = EntityEdit.Properties.Count > 0 ? EntityEdit.Properties.Max(new Func<AddIn.EA.Architecture.ComponentLogic.Property, int>(PropertiesMaxSelector)) + 1 : 1;

            if (property.Encapsulate)
            {
                property.Name = txtAttributeName.Text;
                if (property.Name.Substring(0, 1).CompareTo("_") != 0)
                    property.Name = "_" + property.Name;
                property.Name = property.Name.Substring(0, 2).ToLowerInvariant() + property.Name.Substring(2, property.Name.Length - 2);
            }
            else
                property.Name = txtAttributeName.Text;
            return property;
        }
        private Int32 PropertiesMaxSelector(ComponentLogic.Property item)
        {
            return ((ComponentLogic.StandardProject.PropertyStandardProject)item).Sequence;
        }
        private List<ComponentLogic.RuleSet> GetAttributeRuleSetsList(string value)
        {
            List<ComponentLogic.RuleSet> result = new List<ComponentLogic.RuleSet>();

            if (!String.IsNullOrEmpty(value.Trim()))
            {
                String[] ruleSets = value.Split('+');
                foreach (String item in ruleSets)
                {
                    String ruleSetName;
                    String parameters;

                    ruleSetName = item.Substring(1, item.IndexOf("(") - 1);
                    parameters = item.Substring(item.IndexOf("(") + 1, item.IndexOf(")") - (item.IndexOf("(") + 1));

                    List<ComponentLogic.RuleSetParameter> parametersList = new List<ComponentLogic.RuleSetParameter>();
                    String[] parametersArray = parameters.Split(',');
                    for (int i = 0; i < parametersArray.Length; i++)
                    {
                        parametersList.Add(new ComponentLogic.RuleSetParameter
                        {
                            IsRequired = i == 0,
                            Value = parametersArray[i]
                        });
                    }

                    result.Add(new ComponentLogic.RuleSet
                    {
                        Name = ruleSetName,
                        Parameters = parametersList
                    });
                }
            }

            return result;
        }
        private bool ValidateAttributeForm()
        {
            bool valid = true;
            String message = "Existem erros de validação do atributo, verifique as mensagens abaixo:\r\n";

            if (String.IsNullOrEmpty(txtAttributeName.Text.Trim()))
            {
                valid = false;
                message += "\r\nNome do Atributo.";
            }
            if (AttributeEdit == null &&
                EntityEdit.Properties != null &&
                EntityEdit.Properties.Count > 0 &&
                EntityEdit.Properties.Exists(it => it.Name.CompareTo(txtAttributeName.Text.Trim()) == 0))
            {
                valid = false;
                message += "\r\nO Atributo informado já existe!";
            }
            if (!ValidateRuleSetsFormat(txtAttributeRuleSets.Text.Trim()))
            {
                valid = false;
                message += "\r\nAs RuleSets estão em formato incorreto.\r\nDefina-as como abaixo:\r\n\r\n[RuleSetName(parametros,parametros)]";
            }

            if (!valid)
                MessageBox.Show(message, "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            return valid;
        }
        private bool ValidateRuleSetsFormat(string ruleSetsText)
        {
            bool valid = true;
            if (!String.IsNullOrEmpty(ruleSetsText.Trim()))
            {
                String[] ruleSets = ruleSetsText.Split('+');

                if (ruleSets.Length > 0)
                {
                    Regex rg = new Regex(@"^\[[a-zA-Z]+\([\w" + "\"" + @"\._<>]+(,[\w" + "\"" + @"\._<>]+)*\)\]$");

                    foreach (String item in ruleSets)
                    {
                        if (String.IsNullOrEmpty(item))
                        {
                            valid = false;
                            break;
                        }

                        if (!rg.IsMatch(item))
                        {
                            valid = false;
                            break;
                        }
                    }
                }
            }

            return valid;
        }
        private void CleanEntityAttributesForm()
        {
            try
            {
                txtAttributeInitialValue.Text = String.Empty;
                txtAttributeName.Text = String.Empty;
                txtAttributeNotes.Text = String.Empty;
                txtAttributeRuleSets.Text = String.Empty;
                txtAttributeStereotype.Text = String.Empty;
                //txtAttributeType.Text = String.Empty;
                cmbAttributeTypes.SelectedIndex = 0;
                ckAttributeEncapsulate.Checked = true;
                ckAttributeIsConstant.Checked = false;
                ckAttributeIsStatic.Checked = false;
                ckAttributeUseAsFilter.Checked = false;
                ckAttributeUseAsKey.Checked = false;
                AttributeEdit = null;
                txtAttributeName.Enabled = true;
                txtAttributeName.Focus();
            }
            catch (Exception ex)
            {
                HandleException("Limpar formulário de edição de atributos.", ex);
            }
        }
        private void EditSelectedAttribute()
        {
            try
            {
                if (lstAttributes.SelectedItems.Count == 1)
                {
                    AttributeEdit = EntityEdit.Properties.Find(it => ((ComponentLogic.StandardProject.PropertyStandardProject)it).Sequence.ToString().CompareTo(lstAttributes.SelectedItems[0].SubItems[0].Text) == 0) as ComponentLogic.StandardProject.PropertyStandardProject;
                    if (AttributeEdit != null)
                    {
                        txtAttributeInitialValue.Text = AttributeEdit.InitialValue;
                        txtAttributeName.Text = AttributeEdit.Name;
                        txtAttributeNotes.Text = AttributeEdit.Notes;
                        txtAttributeRuleSets.Text = GetAttributeRuleSetsDescription(AttributeEdit.RuleSets);
                        txtAttributeStereotype.Text = AttributeEdit.Stereotype;
                        cmbAttributeTypes.SelectedItem = AttributeEdit.Type;
                        cmbAttributeScope.SelectedItem = AttributeEdit.Scope.ToString();
                        ckAttributeEncapsulate.Checked = AttributeEdit.Encapsulate;
                        ckAttributeIsConstant.Checked = AttributeEdit.IsConstant;
                        ckAttributeIsStatic.Checked = AttributeEdit.IsStatic;
                        ckAttributeUseAsFilter.Checked = AttributeEdit.ComposeFilter;
                        ckAttributeUseAsKey.Checked = AttributeEdit.ComposeKey;
                        txtAttributeName.Focus();
                        txtAttributeName.Enabled = false;
                    }
                }
                else
                    MessageBox.Show("Selecione um atributo para editar!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                HandleException("Iniciar Edição do Atributo selecionado.", ex);
            }
        }
        private string GetAttributeRuleSetsDescription(List<ComponentLogic.RuleSet> list)
        {
            String description = String.Empty;
            if (list != null && list.Count > 0)
            {
                list.ForEach(it => description += "[" + it.Name + "(" + GetAttributeRuleSetsParametersDescription(it.Parameters) + ")]+");
                if (!String.IsNullOrEmpty(description))
                    description = description.Remove(description.Length - 1, 1);
            }
            return description;
        }
        private string GetAttributeRuleSetsParametersDescription(List<ComponentLogic.RuleSetParameter> list)
        {
            String description = String.Empty;
            if (list != null && list.Count > 0)
            {
                list.ForEach(it => description += it.Value + ",");
                if (!String.IsNullOrEmpty(description))
                    description = description.Remove(description.Length - 1, 1);
            }
            return description;
        }
        private void RemoveSelectedAttribute()
        {
            try
            {
                if (lstAttributes.SelectedItems.Count > 0)
                {
                    if (MessageBox.Show("Deseja realmente remover os atributos selecionados?", "Questão", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        while (lstAttributes.SelectedItems.Count > 0)
                        {
                            EntityEdit.Properties.RemoveAll(it => it.Name.CompareTo(lstAttributes.SelectedItems[0].SubItems[0].Text) == 0);
                            lstAttributes.SelectedItems[0].Remove();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException("Remover Atributo selecionado.", ex);
            }
        }
        private void SaveEntity()
        {
            try
            {
                if (ValidateEntityForm())
                {
                    GetEntityData();

                    this.Tag = EntityEdit;
                    this.Close();
                }
            }
            catch (Exception ex)
            {
                HandleException("Salvar os dados da entidade.", ex);
            }
        }
        private void GetEntityData()
        {
            EntityEdit.HasFilter = EntityEdit.Properties != null && EntityEdit.Properties.Exists(it => it.ComposeFilter);
            EntityEdit.HasKeyFilter = EntityEdit.Properties != null && EntityEdit.Properties.Exists(it => it.ComposeKey);
            EntityEdit.Builders = ucLayers.GetLayerBuilders();
            EntityEdit.Name = txtName.Text.Trim();
            EntityEdit.NamePlural = txtNamePlural.Text.Trim();
            EntityEdit.Notes = txtNotes.Text.Trim();
            EntityEdit.Scope = (ElementScope)Enum.Parse(typeof(ElementScope), cmbScope.SelectedItem.ToString());
        }
        private bool ValidateEntityForm()
        {
            bool valid = true;
            String message = "Existem erros de validação da entidade. Verifique as mensagens abaixo:\r\n";

            if (String.IsNullOrEmpty(txtName.Text.Trim()))
            {
                valid = false;
                message += "\r\nInforme o nome da entidade.";
            }
            if (String.IsNullOrEmpty(txtNamePlural.Text.Trim()))
            {
                valid = false;
                message += "\r\nInforme o nome plural da entidade.";
            }
            if (!ucLayers.IsAnyLayerChecked)
            {
                valid = false;
                message += "\r\nSelecione ao menos uma camada para a entidade.";
            }

            if (!valid)
                MessageBox.Show(message, "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            return valid;
        }
        private void ImportEntityAttributes()
        {
            try
            {
                CreateStandardProjectImportEntityAttributes wd = new CreateStandardProjectImportEntityAttributes(Entities);
                wd.ShowDialog(this);
                if (wd.Tag != null && wd.Tag is List<ComponentLogic.StandardProject.PropertyStandardProject>)
                {
                    List<ComponentLogic.StandardProject.PropertyStandardProject> selectedProperties = wd.Tag as List<ComponentLogic.StandardProject.PropertyStandardProject>;
                    foreach (ComponentLogic.StandardProject.PropertyStandardProject itemProperty in selectedProperties.FindAll(it => !EntityEdit.Properties.Exists(ent => ent.Name.CompareTo(it.Name) == 0)))
                    {
                        itemProperty.Sequence = EntityEdit.Properties.Count > 0 ? EntityEdit.Properties.Max(new Func<AddIn.EA.Architecture.ComponentLogic.Property, int>(PropertiesMaxSelector)) : 1;
                        EntityEdit.Properties.Add((ComponentLogic.Property)itemProperty);
                    }
                    LoadEntityProperties();
                }
            }
            catch (Exception ex)
            {
                HandleException("Abrir importação dos atributos da entidade.", ex);
            }
        }
        private void ValidateExistingEntity()
        {
            try
            {
                if (Entities.Exists(it => it.Name.CompareTo(txtName.Text.Trim()) == 0))
                {
                    MessageBox.Show("A entidade já existe!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    cmdSaveEntity.Enabled = false;
                    txtName.Text = string.Empty;
                    txtName.Focus();
                }
            }
            catch (Exception ex)
            {
                HandleException("Validar a existência da entidade informada!", ex);
            }
        }
        private void ValidateElementNameWriting(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Space)
                e.SuppressKeyPress = true;
        }
        private void AddSelectedRuleSet()
        {
            try
            {
                if (cmbRuleSets.Items.Count > 0)
                {
                    ComponentLogic.RuleSet itemRuleSet = cmbRuleSets.SelectedItem as ComponentLogic.RuleSet;
                    txtAttributeRuleSets.Text += (!String.IsNullOrEmpty(txtAttributeRuleSets.Text.Trim()) ? "+" : String.Empty) +
                                                 "[" + itemRuleSet.Name + "(" + GetRuleSetParametersDescription(itemRuleSet.Parameters) + ")]";
                }
            }
            catch (Exception ex)
            {
                HandleException("Selecionar uma RuleSet.", ex);
            }
        }
        private string GetRuleSetParametersDescription(List<AddIn.EA.Architecture.ComponentLogic.RuleSetParameter> list)
        {
            String result = String.Empty;
            if (list != null && list.Count > 0)
            {
                list.ForEach(it => result += "<" + it.Name + ":" + it.Type.ToString() + ">,");
                if (!String.IsNullOrEmpty(result))
                    result = result.Remove(result.Length - 1, 1);
            }
            return result;
        }
        #endregion
    }
}
